package com.estun.backroundManagementService.modules.material.service.impl;

import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.estun.backroundManagementService.config.CustomApiException;
import com.estun.backroundManagementService.config.factoryMes.ApiCaller;
import com.estun.backroundManagementService.domain.factoryMes.request.AgvAutomaticFeedingRequest;
import com.estun.backroundManagementService.domain.factoryMes.request.MaterialBindRequest;
import com.estun.backroundManagementService.domain.factoryMes.request.PassStationRequest;
import com.estun.backroundManagementService.domain.factoryMes.request.base.BaseMesRequest;
import com.estun.backroundManagementService.domain.factoryMes.request.base.BaseMesResponse;
import com.estun.backroundManagementService.config.properties.ApiInfoProperties;
import com.estun.backroundManagementService.config.properties.LineInfoProperties;
import com.estun.backroundManagementService.domain.BaseResponse;
import com.estun.backroundManagementService.domain.factoryMes.request.MaterialSyncRequest;
import com.estun.backroundManagementService.modules.material.mapper.MaterialMapper;
import com.estun.backroundManagementService.modules.material.model.Material;
import com.estun.backroundManagementService.modules.material.model.MaterialBatch;
import com.estun.backroundManagementService.modules.material.model.RebindHis;
import com.estun.backroundManagementService.modules.material.service.MaterialBatchService;
import com.estun.backroundManagementService.modules.material.service.MaterialService;
import com.estun.backroundManagementService.modules.material.service.RebindHisService;
import com.estun.backroundManagementService.modules.position.model.Position;
import com.estun.backroundManagementService.modules.position.service.PositionService;
import com.estun.backroundManagementService.modules.product.model.Product;
import com.estun.backroundManagementService.modules.product.service.ProductService;
import com.estun.backroundManagementService.modules.workOrder.dto.WorkOrderEntity;
import com.estun.backroundManagementService.modules.workOrder.service.WorkOrderService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.web.client.RestClientException;

import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

@Slf4j
@Service
@AllArgsConstructor
public class MaterialServiceImpl extends ServiceImpl<MaterialMapper, Material> implements MaterialService {
    private final ApiCaller apiCaller;
    private final ApiInfoProperties apiInfoProperties;
    private final LineInfoProperties lineInfoProperties;
    private final MaterialBatchService materialBatchService;
    private final WorkOrderService workOrderService;
    private final ProductService productService;
    private final RebindHisService rebindHisService;
    private final PositionService positionService;

    @Override
    public BaseResponse sync() {
        MaterialSyncRequest materialSyncRequest = new MaterialSyncRequest(lineInfoProperties);
        materialSyncRequest.setRequestTime(LocalDateTime.now());
        materialSyncRequest.setSyncLine(false);
        long count = count();
        if (count != 0) {
            materialSyncRequest.setUpdateTime(LocalDateTime.now());
            log.info("当前数据不为空，只同步增量数据，设置updateTime为当前时间");
        }
        materialSyncRequest.setLineCode(lineInfoProperties.getLineCode());
        log.info("【物料主数据同步】调用入参-1:{}", materialSyncRequest);
        BaseMesResponse<JSONObject> post = apiCaller.post(apiInfoProperties.getSyncMaterial(), materialSyncRequest, BaseMesResponse.class);
        log.info("【物料主数据同步】工厂MES返回结果:{}，数据数量:{}", post.isSuccess(), post.getData() != null ? post.getData().size() : "null");
        List<JSONObject> data = post.getData();

        if (CollectionUtils.isEmpty(data)) {
            return BaseResponse.success("本次没有同步到数据");
        }
        log.info("data.get(0):{}", data.get(0));
        data.forEach(row -> {
            row.put("materialId", row.get("id"));
            row.put("id", null);
        });
        List<Material> materialList = data.stream().map(row -> {
            return JSON.toJavaObject(row, Material.class);
        }).collect(Collectors.toList());
        materialList.forEach(Material::setIdNull);

        log.info("【物料主数据同步】本次同步到{}条数据", data.size());
        boolean b = saveBatch(materialList);
        if (b) {
            return BaseResponse.success("【物料主数据同步】同步成功");
        } else {
            return BaseResponse.error("【物料主数据同步】同步失败");
        }
    }

    @Override
    public BaseResponse bind(String productCode, String materialType, String materialCode, String opCode) {
//        log.info("【物料绑定 触发过站记录推送】开始任务");
//        pushPassStationTask.pushPassStation();
//        log.info("【物料绑定 触发过站记录推送】结束任务");
        WorkOrderEntity inProcessSchedule = workOrderService.getInProcessSchedule();
        MaterialBindRequest materialBindRequest = new MaterialBindRequest();
        materialBindRequest.setLineCode(lineInfoProperties.getLineCode());
        materialBindRequest.setStationCode(opCode);
        materialBindRequest.setOrderCode(inProcessSchedule.getOrderCode());
        materialBindRequest.setScheduleCode(inProcessSchedule.getScheduleCode());
        materialBindRequest.setSnNumber(productCode);
        materialBindRequest.setAssemblyMaterialQty(1);
        //todo
        materialBindRequest.setAssemblySort("1");
        materialBindRequest.setAssemblyTime(DateUtil.format(LocalDateTime.now(), "yyyy-MM-dd HH:mm:ss"));

        Material material = getMaterialByName(materialType);
        if (material != null) {
            materialBindRequest.setAssemblyMaterialCode(material.getMaterialCode());
            materialBindRequest.setAssemblyMaterialName(material.getMaterialName());
            materialBindRequest.setAssemblyMaterialVersion(material.getMaterialVersion());
        }
        materialBindRequest.setAssemblyMaterialSn(materialCode);
        MaterialBatch materialBatch = getMaterialBatchByName(materialType);
        if (materialBatch != null) {
            materialBindRequest.setMaterialBatchNo(materialBatch.getBatchNumber());
        }
        BaseMesRequest<MaterialBindRequest> request = new BaseMesRequest<>();
        request.setData(Collections.singletonList(materialBindRequest));
        BaseMesResponse post = null;
        String factoryRes = "";
        try {
            post = apiCaller.post(apiInfoProperties.getMaterialBind(), request, BaseMesResponse.class);
            log.info("【物料绑定】工厂MES返回:{}", post);
            if (!post.isSuccess()) {
                factoryRes = "工厂推送绑定失败";
            } else {
                factoryRes = "工厂推送绑定成功";
            }
        } catch (Exception e) {
            factoryRes = "工厂推送绑定异常";
            log.error("【物料绑定】推送工厂mes物料绑定异常:{}", e.getMessage());
        }

        String bindRes = "";
        try {
            doBind(productCode, materialType, materialCode);
            bindRes = "绑定成功";
            return BaseResponse.success(bindRes + "," + factoryRes);
        } catch (Exception e) {
            log.error("【物料绑定】物料绑定异常:{}", e.getMessage());
            bindRes = "绑定异常";
            return BaseResponse.error(bindRes + "," + factoryRes);
        }
    }

    @Override
    public Material getFirstOneByCode(String materialCode) {
        List<Material> list = list(new QueryWrapper<Material>().eq("material_code", materialCode));
        return list.isEmpty() ? null : list.get(0);
    }

    // 调用工厂MES接口，启用重试机制
    @Override
    @Retryable(value = {CustomApiException.class, RestClientException.class}, maxAttempts = 3, backoff = @Backoff(delay = 60000))
    public void agv(String materialCode, String toPointCode) {
        AgvAutomaticFeedingRequest agvAutomaticFeedingRequest = new AgvAutomaticFeedingRequest();
        agvAutomaticFeedingRequest.setFactoryCode(lineInfoProperties.getFactoryCode());
        agvAutomaticFeedingRequest.setInterfaceCode("Station_Call_Material");
        agvAutomaticFeedingRequest.setMesUrl("https://sz-mes-test.byd.com/");
        agvAutomaticFeedingRequest.setMesVersion("3");
        agvAutomaticFeedingRequest.setRemote("10.28.20.13");
        agvAutomaticFeedingRequest.setServiceId(apiInfoProperties.getAgvAutomaticFeeding().getServiceId());
        agvAutomaticFeedingRequest.setTimer(System.currentTimeMillis());
        agvAutomaticFeedingRequest.setType("POST");
        agvAutomaticFeedingRequest.setUuid(UUID.randomUUID().toString().replace("-", ""));
        AgvAutomaticFeedingRequest.Body body = new AgvAutomaticFeedingRequest.Body();
        body.setMaterialCode(materialCode);
        body.setToPointCode(toPointCode);
        agvAutomaticFeedingRequest.setBody(body);

        long beginTime = System.currentTimeMillis();
        log.info("【工厂接口调用】api serviceId:{} 入参:{}", apiInfoProperties.getAgvAutomaticFeeding().getServiceId(), agvAutomaticFeedingRequest);

        // 调用接口
        ResponseEntity<String> responseEntity = apiCaller.callApi(apiInfoProperties.getAgvAutomaticFeeding().getUrl(), agvAutomaticFeedingRequest, HttpMethod.POST);
        log.info("【工厂接口调用】api serviceId:{} 返回:{} 耗时:{}",
                apiInfoProperties.getAgvAutomaticFeeding().getServiceId(), responseEntity, System.currentTimeMillis() - beginTime);

        // 如果接口没有返回任何内容，则抛出异常以便重试
        if (responseEntity == null || !responseEntity.hasBody() || !responseEntity.getStatusCode().is2xxSuccessful()) {
            log.error("接口没有返回任何内容或状态码非成功状态，可能超时");
            throw new CustomApiException("接口没有返回任何内容或状态码非成功状态，可能超时");
        }

        String code = "";
        try {
            JSONObject jsonObject = JSON.parseObject(responseEntity.getBody());
            JSONObject data = jsonObject.getJSONObject("data");
            code = data.getString("code");
        } catch (Exception e) {
            log.error("解析接口返回值时发生 JSON 错误", e);
            throw new CustomApiException("解析接口返回值时发生 JSON 错误", e);
        }
        if (!"000000".equals(code)) {
            log.error("接口返回值中指定 code 字段不为 000000");
            throw new CustomApiException("接口返回值中指定 code 字段不为 000000");
        }
    }

    // 重试三次后仍然失败的回退方法
    @Recover
    public void recover(CustomApiException e, String materialCode, String toPointCode) {
        log.error("工厂MES接口调用失败，重试已耗尽。MaterialCode: {}, ToPointCode: {}，错误信息: {}", materialCode, toPointCode, e.getMessage());
        // 执行降级处理逻辑，例如发送告警或进行其他补偿
    }

    /**
     * @param productCode                                           产品码
     * @param materialType"上油路板"、"下油路板"、"主压力电磁阀"、"过冷保护电磁阀"、"离合器电磁阀"
     * @param materialCode                                          物料码
     */
    private void doBind(String productCode, String materialType, String materialCode) {
        List<Product> list;
        if ("上油路板".equals(materialType)) {
            list = productService.list(new QueryWrapper<Product>().eq("upper_code", materialCode));
        } else {
            list = productService.list(new QueryWrapper<Product>().eq("product_no", productCode));
        }
        if (CollectionUtils.isEmpty(list)) {
            log.error("【物料绑定】没有获取到产品 结束绑定 productCode:{} materialType:{} materialCode:{}", productCode, materialType, materialCode);
            return;
        } else if (list.size() > 1) {
            log.error("【物料绑定】获取到多个产品 productCode:{}materialType:{}materialCode:{}", productCode, materialType, materialCode);
        }
        Product product = list.get(0);
        Long productId = product.getId();
        Product updateProduct = new Product();
        updateProduct.setId(productId);

        String oldCode = null;
        switch (materialType) {
            case "上油路板": {
                oldCode = product.getUpperCode();
                updateProduct.setProductNo(productCode);
                break;
            }
            case "下油路板": {
                oldCode = product.getLowerCode();
                updateProduct.setLowerCode(materialCode);
                break;
            }
            case "主压力电磁阀": {
                oldCode = product.getEmvA();
                updateProduct.setEmvA(materialCode);
                break;
            }
            case "过冷保护电磁阀": {
                oldCode = product.getEmvB();
                updateProduct.setEmvB(materialCode);
                break;
            }
            case "离合器电磁阀": {
                oldCode = product.getEmvC();
                updateProduct.setEmvC(materialCode);
                break;
            }
        }
        productService.updateById(updateProduct);
        log.info("【物料绑定】 updateProduct:{}", updateProduct);
        //判断为重新绑定
        if (StringUtils.isNotBlank(oldCode)
                && !oldCode.equals(materialCode)) {
            //初始化rebind记录
            RebindHis reBindHis = new RebindHis();
            reBindHis.setProductId(productId);
            reBindHis.setType(materialType);
            reBindHis.setNewSn(materialCode);
            reBindHis.setOldSn(oldCode);
            rebindHisService.save(reBindHis);
        }
    }


    /**
     * 获取物料
     */
    private Material getMaterialByName(String materialType) {
        switch (materialType) {
            case "上油路板": {
                String opCode = "_BOP010";
                String keyWord = "上油路板";
                return getMaterial(opCode, keyWord);
            }

            case "下油路板": {
                String opCode = "_OP010";
                String keyWord = "上油路板";
                return getMaterial(opCode, keyWord);
            }

            case "主压力电磁阀": {
                String opCode = "_OP070-1";
                String keyWord = "主压力";
                return getMaterial(opCode, keyWord);
            }

            case "过冷保护电磁阀": {
                String opCode = "_OP070-1";
                String keyWord = "过冷保护";
                return getMaterial(opCode, keyWord);
            }

            case "离合器电磁阀": {
                String opCode = "_OP070-1";
                String keyWord = "离合器";
                return getMaterial(opCode, keyWord);
            }

        }
        return null;
    }

    private Material getMaterial(String opCode, String keyWord) {
        Position position = positionService.getOne(new QueryWrapper<Position>().like("station_id", "\\" + opCode));
        List<String> materialIdList = position.getMaterialIdList();
        List<Material> materialList = list(new QueryWrapper<Material>().in("material_code", materialIdList));
        return materialList.stream()
                .filter(material -> material.getMaterialName().contains(keyWord))
                .findFirst().orElse(null);
    }


    /**
     * 获取物料批次 todo
     */
    private MaterialBatch getMaterialBatchByName(String materialType) {

        switch (materialType) {
            case "上油路板":
            case "下油路板":
            case "主压力电磁阀":
            case "过冷保护电磁阀":
            case "离合器电磁阀":
        }
        //get
        List<MaterialBatch> list = materialBatchService.list();
        return null;
    }

}
